<template>
  <div class="container">
    <!-- 侧边栏 -->
    <aside :class="{ 'sidebar-collapsed': sidebarCollapsed }">
      <div class="sidebar-header">
        <button @click="startNewChat" class="new-chat-btn">
          <span v-if="!sidebarCollapsed">新的对话</span> ➕
        </button>
        <button class="collapse-btn" @click="sidebarCollapsed = !sidebarCollapsed">
          ☰
        </button>
      </div>
      <div class="history-list">
        <div v-for="chat in chatHistory" :key="chat.id" class="history-item"
          :class="{ active: activeChatId === chat.id }" @click="switchChat(chat.id)">
          {{ chat.title || chat.messages[0]?.content?.substring(0, 20) || '新对话' }}
        </div>
      </div>
    </aside>

    <!-- 主聊天区域 -->
    <main>
      <div class="chat-container">
        <div class="messages" ref="messagesContainer">
          <!-- 显示所有消息 -->
          <template v-for="(message, index) in activeChat.messages" :key="index">
            <!-- 用户消息 -->
            <div v-if="message.role === 'user'" :class="['message', message.role]">
              <div class="message-content">
                <div>{{ message.content }}</div>
              </div>
            </div>

            <!-- 助手消息 -->
            <div v-else-if="message.role === 'assistant'" :class="['message', message.role]">
              <div class="message-content">
                <!-- 文本回答 -->
                <div v-if="message.content" class="text-answer">
                  {{ message.content }}
                </div>

                <!-- 图谱回答 - 只有当有图数据时才显示 -->
                <div v-if="hasGraphData(message)" class="graph-container"
                  :ref="el => { if (el) graphContainers[message.id] = el }"></div>
              </div>
            </div>
          </template>
        </div>

        <!-- 输入区域 -->
        <div class="input-area">
          <div style="display: flex; gap: 2px;">
            <textarea v-model="inputMessage" @keydown.enter.exact.prevent="sendMessage"
              placeholder="输入你的问题..."></textarea>
            <button @click="sendMessage" :disabled="isLoading || fileUploading">
              {{ isLoading ? '发送中...' : '发送' }}
            </button>
          </div>
          <div v-if="fileUploading" class="upload-hint">
            文件上传中，请稍候...
          </div>
          <div v-else-if="fileUploadError" class="upload-hint error">
            文件上传失败: {{ fileUploadError }}
          </div>
        </div>
      </div>
    </main>

    <!-- 文件上传区域 -->
    <aside class="file-upload-section" @dragover.prevent="handleDragOver" @dragleave="handleDragLeave"
      @drop.prevent="handleDrop" :class="{ 'drag-over': isDragging }">
      <div class="upload-header">
        <h3>文件上传区域</h3>
        <p>拖放文件到此处或点击按钮上传</p>
      </div>

      <div class="upload-content">
        <button class="upload-btn" @click="triggerFileInput">
          选择文件
        </button>
        <input type="file" ref="fileInput" @change="handleFileUpload" hidden
          accept=".pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document">

        <!-- 文件信息显示 -->
        <div v-if="selectedFile" class="file-info">
          <div class="file-icon">📄</div>
          <div class="file-details">
            <div class="file-name">{{ selectedFile.name }}</div>
            <div class="file-status" v-if="fileUploading">上传中... {{ uploadProgress }}%</div>
            <div class="file-status success" v-if="fileUploaded">上传成功！</div>
            <div class="file-status error" v-if="fileUploadError">{{ fileUploadError }}</div>
          </div>
          <button class="remove-file-btn" @click="removeFile">×</button>
        </div>

        <!-- 上传进度条 -->
        <div v-if="fileUploading" class="progress-bar">
          <div class="progress" :style="{ width: uploadProgress + '%' }"></div>
        </div>

        <!-- 支持的文件类型提示 -->
        <div class="support-hint">
          支持的文件类型: PDF, DOC, DOCX
        </div>
      </div>

      <!-- 拖拽提示 -->
      <div v-if="isDragging" class="drag-hint">
        松开鼠标上传文件
      </div>
    </aside>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, reactive, nextTick } from 'vue'
import { Network } from "vis-network";
import { DataSet } from "vis-data";

// 状态管理
const messagesContainer = ref(null)
const sidebarCollapsed = ref(false)
const inputMessage = ref('')
const isLoading = ref(false)
const chatHistory = ref([])
const activeChatId = ref(null)
const graphContainers = ref({}) // 图谱容器引用（使用消息ID作为键）
const networkInstances = reactive({}) // 图谱实例存储
const fileInput = ref(null) // 文件输入引用
const selectedFile = ref(null) // 选择的文件
const isDragging = ref(false) // 拖拽状态
const fileUploading = ref(false) // 文件上传中
const fileUploaded = ref(false) // 文件已上传
const fileUploadError = ref('') // 上传错误信息
const uploadProgress = ref(0) // 上传进度
// 检查消息是否有图谱数据
const hasGraphData = (message) => {
  return message.graphData?.nodes?.length > 0 
      && message.graphData?.edges?.length > 0;
}
// 处理拖拽进入
const handleDragOver = (event) => {
  event.preventDefault()
  isDragging.value = true
}

// 处理拖拽离开
const handleDragLeave = () => {
  isDragging.value = false
}

// 触发文件选择对话框
const triggerFileInput = () => {
  fileInput.value.click()
}

// 处理文件选择
const handleFileUpload = (event) => {
  const files = event.target.files
  if (files.length > 0) {
    // 验证文件类型
    const file = files[0]
    const validTypes = [
      'application/pdf',
      'application/msword',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    ]

    if (validTypes.includes(file.type) ||
      file.name.endsWith('.pdf') ||
      file.name.endsWith('.doc') ||
      file.name.endsWith('.docx')) {
      // 只允许一个文件
      selectedFile.value = file
      uploadFile(file)
    } else {
      fileUploadError.value = '只支持 PDF、DOC 和 DOCX 格式的文件'
      fileInput.value.value = '' // 重置input值
    }
  }
}

// 处理文件拖放
const handleDrop = (event) => {
  isDragging.value = false
  const files = event.dataTransfer.files
  if (files.length > 0) {
    // 只处理第一个文件
    const file = files[0]

    // 检查文件类型
    const validTypes = [
      'application/pdf',
      'application/msword',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    ]

    if (validTypes.includes(file.type) ||
      file.name.endsWith('.pdf') ||
      file.name.endsWith('.doc') ||
      file.name.endsWith('.docx')) {
      selectedFile.value = file
      uploadFile(file)
    } else {
      fileUploadError.value = '只支持 PDF、DOC 和 DOCX 格式的文件'
    }
  }
}

// 上传文件到后端
const uploadFile = async (file) => {
  if (!file) return

  // 重置状态
  fileUploading.value = true
  fileUploaded.value = false
  fileUploadError.value = ''
  uploadProgress.value = 0

  // 添加系统消息
  const systemMessage = {
    id: Date.now(),
    role: 'system',
    content: '文件上传中...',
    isUploading: true
  }
  activeChat.value.messages.push(systemMessage)
  scrollToBottom()

  const formData = new FormData()
  formData.append('file', file)

  try {
    // 使用fetch API上传文件
    const response = await fetch('http://127.0.0.1:8080/main/pdfAnalyze', {
      method: 'POST',
      body: formData
    })

    if (!response.ok) {
      throw new Error(`上传失败: ${response.status} ${response.statusText}`)
    }

    // 移除系统消息
    const lastIndex = activeChat.value.messages.length - 1;
    if (activeChat.value.messages[lastIndex].isUploading) {
      activeChat.value.messages.splice(lastIndex, 1);
    }

    // 添加用户消息（固定为"解析文件"）
    const userMessage = {
      id: Date.now(),
      role: 'user',
      content: '解析文件'
    }
    activeChat.value.messages.push(userMessage)

    // 添加助手消息（包含文本和图谱）
    const assistantMessage = {
      id: Date.now() + 1,
      role: 'assistant',
      content: '文件解析完成',
      graphData: { nodes: [], edges: [] }
    }
    activeChat.value.messages.push(assistantMessage)

    // 获取可读流
    const reader = response.body.getReader()
    const decoder = new TextDecoder()
    let buffer = ''

    // 处理流式数据
    while (true) {
      const { done, value } = await reader.read()
      if (done) {
        fileUploading.value = false
        fileUploaded.value = true
        break
      }

      // 解码并添加到缓冲区
      buffer += decoder.decode(value, { stream: true })

      // 处理缓冲区中完整的JSON对象（以换行符分隔）
      let newlineIndex
      while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
        // 提取一个完整的JSON对象
        const jsonStr = buffer.substring(0, newlineIndex)
        buffer = buffer.substring(newlineIndex + 1)

        if (jsonStr.trim() === '') continue

        try {
          const data = JSON.parse(jsonStr)

          // 更新图谱数据
          if (data.type === 'nodes') {
            assistantMessage.graphData.nodes.push(...data.nodes)
          } else if (data.type === 'edges') {
            assistantMessage.graphData.edges.push(...data.edges)
          }

          // 渲染图谱
          nextTick(() => {
            renderSingleGraph(assistantMessage.id)
            scrollToBottom()
          })
        } catch (e) {
          console.error('JSON解析错误:', e, '数据:', jsonStr)
        }
      }
    }
  } catch (error) {
    console.error('上传失败:', error)
    fileUploading.value = false
    fileUploadError.value = error.message || '文件上传失败'

    // 移除系统消息
    const lastIndex = activeChat.value.messages.length - 1;
    if (activeChat.value.messages[lastIndex].isUploading) {
      activeChat.value.messages.splice(lastIndex, 1);
    }
  }
}

// 移除已选择的文件
const removeFile = () => {
  selectedFile.value = null
  fileInput.value.value = '' // 重置input值
  fileUploaded.value = false
  fileUploading.value = false
  fileUploadError.value = ''
  uploadProgress.value = 0
}

// 当前激活的聊天
const activeChat = computed(() => {
  return chatHistory.value.find(chat => chat.id === activeChatId.value) || {
    id: Date.now(),
    title: '新对话',
    messages: []
  }
})

// 增强版滚动方法
const scrollToBottom = () => {
  if (!messagesContainer.value) return
  nextTick(() => {
    const container = messagesContainer.value
    const isScrolledToBottom =
      container.scrollHeight - container.clientHeight <= container.scrollTop + 1

    if (isScrolledToBottom) {
      container.scrollTo({
        top: container.scrollHeight,
        behavior: 'auto'
      })
    }
  })
}

// 初始化新聊天
function startNewChat() {
  if (chatHistory.value.length > 0 && chatHistory.value[0].messages.length == 0) return
  const newChat = {
    id: Date.now(),
    title: '',
    messages: []
  }
  chatHistory.value.unshift(newChat)
  activeChatId.value = newChat.id
}

// 渲染单个图表
const renderSingleGraph = (msgId, attempt = 0) => {
  if (attempt > 5) { // 最多重试5次
    console.error(`无法为消息${msgId}找到图谱容器`);
    return;
  }
  console.log(activeChat.value.messages)
  const container = graphContainers.value[msgId];
  if (!container) {
    // 等待下一个tick再重试
    nextTick(() => renderSingleGraph(msgId, attempt + 1));
    return;
  }
  // 销毁旧实例
  if (networkInstances[msgId]) {
    networkInstances[msgId].destroy()
  }

  // 查找对应的消息
  const message = activeChat.value.messages.find(m => m.id === msgId)
  
  
  if (!message || !hasGraphData(message)) return  // 确保有数据才渲染
  
  const graphData = message.graphData
  
  // 转换整型ID为字符串，适配vis-network
  const nodes = new DataSet(graphData.nodes.map(n => ({
    id: String(n.ID),
    label: n.Label,
    group: n.Type,
    title: formatTooltip(n)
  })))

  const edges = new DataSet(graphData.edges.map(e => ({
    id: String(e.ID),
    from: String(e.Source),
    to: String(e.Target),
    label: e.Label,
    arrows: 'to'
  })))

  // 配置选项
  const options = {
    nodes: {
      shape: 'dot',
      size: 25,
      font: { size: 12 },
      borderWidth: 2
    },
    edges: {
      width: 2,
      smooth: true
    },
    physics: {
      stabilization: true
    },
    interaction: {
      hover: true
    }
  }

  // 创建实例
  networkInstances[msgId] = new Network(container, { nodes, edges }, options)
  // 添加重绘逻辑
  if (networkInstances[msgId]) {
    networkInstances[msgId].redraw()
  }
}

// 修改工具提示
const formatTooltip = (node) => {
  return `
    <div class="graph-tooltip">
      <h4>${node.Label}</h4>
      <p>类型: ${node.Type}</p>
    </div>
  `
}

// 切换聊天
function switchChat(chatId) {
  activeChatId.value = chatId
}

// 发送消息
async function sendMessage() {
  if (!inputMessage.value.trim() || isLoading.value || fileUploading.value) return

  // 添加用户消息
  const userMessage = {
    id: Date.now(), // 为每条消息添加唯一ID
    role: 'user',
    content: inputMessage.value,
  }
  activeChat.value.messages.push(userMessage)
  inputMessage.value = ''
  scrollToBottom()

  try {
    isLoading.value = true

    // 创建助手消息（包含文本和图谱）
    const assistantMessage = {
      id: Date.now() + 1, // 唯一ID
      role: 'assistant',
      content: '', // 初始为空
      graphData: { nodes: [], edges: [] } // 初始空图谱
    }
    activeChat.value.messages.push(assistantMessage)
    scrollToBottom()

    // 使用统一的API
    const response = await fetch(`http://127.0.0.1:8080/main/answer`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ question: userMessage.content })
    })

    // 检查响应是否有效
    if (!response.ok) {
      throw new Error(`请求失败: ${response.status} ${response.statusText}`)
    }

    // 获取可读流
    const reader = response.body.getReader()
    const decoder = new TextDecoder()
    let buffer = ''

    while (true) {
      const { done, value } = await reader.read()
      if (done) {
        isLoading.value = false
        nextTick(() => {
          scrollToBottom()
          renderSingleGraph(assistantMessage.id)
        })
        break
      }

      // 解码并添加到缓冲区
      buffer += decoder.decode(value, { stream: true })

      // 处理缓冲区中完整的JSON对象（以换行符分隔）
      let newlineIndex
      while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
        // 提取一个完整的JSON对象
        const jsonStr = buffer.substring(0, newlineIndex)
        buffer = buffer.substring(newlineIndex + 1)

        if (jsonStr.trim() === '') continue

        try {
          const data = JSON.parse(jsonStr)
          const lastMsgIndex = activeChat.value.messages.length - 1

          if (activeChat.value.messages[lastMsgIndex].role === 'assistant') {
            // 处理文本内容
            if (data.type === 'text') {
              assistantMessage.content += data.content
            }

            // 处理图谱数据
            if (data.type === 'nodes') {
              assistantMessage.graphData.nodes.push(...data.nodes)
            } else if (data.type === 'edges') {
              assistantMessage.graphData.edges.push(...data.edges)
            }

            // 渐进式渲染
            nextTick(() => {
              scrollToBottom()
              if (data.type === 'nodes' || data.type === 'edges') {
                renderSingleGraph(assistantMessage.id)
              }
            })
          }
        } catch (e) {
          console.error('JSON解析错误:', e, '数据:', jsonStr)
        }
      }
    }

  } catch (error) {
    console.error('请求失败:', error)
    activeChat.value.messages.push({
      id: Date.now(),
      role: 'assistant',
      content: '请求失败，请重试',
    })
    nextTick(scrollToBottom)
    isLoading.value = false
  }
}

// 初始化
onMounted(() => {
  if (chatHistory.value.length === 0) {
    startNewChat()
  }
})
</script>
<style scoped>
.container {
  display: flex;
  height: 98vh;
  background: #f0f2f5;
  overflow: hidden;
  /* 防止整体滚动 */
}

aside {
  width: 260px;
  background: #202123;
  color: white;
  transition: width 0.3s;
  display: flex;
  flex-direction: column;
}

.sidebar-collapsed {
  width: 80px;
}

.sidebar-header {
  padding: 12px;
  border-bottom: 1px solid #343541;
}

.new-chat-btn {
  background: #343541;
  color: white;
  border: 1px solid rgba(255, 255, 255, 0.2);
  padding: 12px;
  width: 100%;
  border-radius: 5px;
  cursor: pointer;
  margin-bottom: 12px;
}

.history-list {
  flex: 1;
  overflow-y: auto;
}

.history-item {
  padding: 12px;
  cursor: pointer;
  border-radius: 5px;
  margin: 4px;
}

.history-item:hover {
  background: #343541;
}

.history-item.active {
  background: #343541;
}

main {
  flex: 1;
  display: flex;
  flex-direction: column;
  position: relative;
  /* 为固定输入框定位 */
}

.chat-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  max-width: 800px;
  margin: 0 auto;
  width: 100%;
  height: 100%;
  padding: 20px;
}

.messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  margin-bottom: 80px;
}

.message {
  margin: 12px 0;
  display: flex;
}

.message.user {
  justify-content: flex-end;
}

.message-content {
  max-width: 70%;
  padding: 12px 16px;
  border-radius: 8px;
  background: white;
}

.message.assistant .message-content {
  background: #e8e8e8;
}

.input-area {
  position: sticky;
  /* 粘性定位 */
  bottom: 0;
  background: #f0f2f5;
  padding: 20px;
  border-top: 1px solid #ddd;
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 100;
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
}

textarea {
  flex: 1;
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 8px;
  resize: none;
  height: 80px;
}

button {
  padding: 12px 24px;
  background: #10a37f;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

button:disabled {
  background: #cccccc;
  cursor: not-allowed;
}

.loading {
  color: #666;
  padding: 12px;
  text-align: center;
}

.graph-container {
  height: 400px;
  width: 100%;
  margin-top: 12px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

/* 工具提示样式 */
:deep(.vis-tooltip) {
  max-width: 300px !important;
  background: white !important;
  border: 1px solid #ddd !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
  padding: 12px !important;
}

.graph-tooltip h4 {
  margin: 0 0 8px 0;
  color: #333;
}

.graph-tooltip p {
  margin: 4px 0;
  color: #666;
}

/* 文件上传区域样式 */
.file-upload-section {
  width: 300px;
  background: white;
  border-left: 1px solid #ddd;
  padding: 20px;
  display: flex;
  flex-direction: column;
  position: relative;
  transition: all 0.3s ease;
  border: 2px dashed transparent;
}

.file-upload-section.drag-over {
  border-color: #10a37f;
  background-color: rgba(16, 163, 127, 0.1);
}

.upload-header {
  margin-bottom: 20px;
  text-align: center;
}

.upload-header h3 {
  margin: 0 0 8px 0;
  color: #333;
}

.upload-header p {
  margin: 0;
  color: #666;
  font-size: 0.9em;
}

.upload-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.upload-btn {
  padding: 12px 24px;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 16px;
  margin-bottom: 20px;
  transition: background 0.3s;
}

.upload-btn:hover {
  background: #2980b9;
}

.file-info {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 12px;
  background: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #e9ecef;
  margin-bottom: 15px;
}

.file-icon {
  font-size: 24px;
  margin-right: 12px;
}

.file-details {
  flex: 1;
}

.file-name {
  font-weight: bold;
  margin-bottom: 4px;
  color: #202123;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-status {
  font-size: 0.85em;
  color: #666;
}

.file-status.success {
  color: #27ae60;
}

.file-status.error {
  color: #e74c3c;
}

.remove-file-btn {
  background: none;
  border: none;
  color: #e74c3c;
  cursor: pointer;
  font-size: 20px;
  padding: 0 5px;
  margin-left: 5px;
}

.remove-file-btn:hover {
  color: #c0392b;
}

.progress-bar {
  width: 100%;
  height: 8px;
  background: #e9ecef;
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 15px;
}

.progress {
  height: 100%;
  background: #10a37f;
  transition: width 0.3s;
}

.support-hint {
  font-size: 0.85em;
  color: #666;
  text-align: center;
  margin-top: 10px;
}

.drag-hint {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(255, 255, 255, 0.9);
  color: #10a37f;
  font-weight: bold;
  font-size: 1.2em;
  z-index: 10;
  pointer-events: none;
  border-radius: 8px;
}

.upload-hint {
  color: #666;
  font-size: 0.9em;
  margin-top: 8px;
}

.upload-hint.error {
  color: #e74c3c;
}

/* 添加换行处理 */
.message-content {
  word-break: break-word;
}

.text-answer {
  margin-bottom: 20px;
}
</style>